#include "PropertyManager.h"
#include "StringFormatter.h"

#include "LeIpc2581/Xform.h"

#include <QtMath>
#include <QDebug>

class TransformPropertyType
{
};
Q_DECLARE_METATYPE(TransformPropertyType)

PropertyManager::PropertyManager(QObject *parent):
    QtVariantPropertyManager(parent)
{
    connect(this, &PropertyManager::valueChanged,
            this, &PropertyManager::slotValueChanged);
    connect(this, &PropertyManager::propertyDestroyed,
            this, &PropertyManager::slotPropertyDestroyed);
}

PropertyManager::~PropertyManager()
{

}

int PropertyManager::transformTypeId()
{
    //return qMetaTypeId<TransformPropertyType>();
    return QVariant::Transform;
}

void PropertyManager::slotValueChanged(QtProperty *property, const QVariant &value)
{
    if (m_transformMirrorToProperty.contains(property))
    {
        auto transformProperty = m_transformMirrorToProperty.value(property);
        auto transformValue = this->value(transformProperty).value<QTransform>();
        // FIXME
        Q_UNUSED(value)
        setValue(transformProperty, transformValue);
    }
    else if (m_transformRotationToProperty.contains(property))
    {
        auto transformProperty = m_transformMirrorToProperty.value(property);
        auto transformValue = this->value(transformProperty).value<QTransform>();
        // FIXME
        Q_UNUSED(value)
        setValue(transformProperty, transformValue);
    }
    else if (m_transformScaleToProperty.contains(property))
    {
        auto transformProperty = m_transformMirrorToProperty.value(property);
        auto transformValue = this->value(transformProperty).value<QTransform>();
        // FIXME
        Q_UNUSED(value)
        setValue(transformProperty, transformValue);
    }
    else if (m_transformXOffsetToProperty.contains(property))
    {
        auto transformProperty = m_transformMirrorToProperty.value(property);
        auto transformValue = this->value(transformProperty).value<QTransform>();
        // FIXME
        Q_UNUSED(value)
        setValue(transformProperty, transformValue);
    }
    else if (m_transformYOffsetToProperty.contains(property))
    {
        auto transformProperty = m_transformMirrorToProperty.value(property);
        auto transformValue = this->value(transformProperty).value<QTransform>();
        // FIXME
        Q_UNUSED(value)
        setValue(transformProperty, transformValue);
    }
}

void PropertyManager::slotPropertyDestroyed(QtProperty *property)
{
    if (m_transformMirrorToProperty.contains(property))
    {
        auto tProperty = m_transformMirrorToProperty.value(property);
        m_propertyTotransformData[tProperty].mirror = nullptr;
        m_transformMirrorToProperty.remove(property);
    }
    else if (m_transformRotationToProperty.contains(property))
    {
        auto tProperty = m_transformRotationToProperty.value(property);
        m_propertyTotransformData[tProperty].rotation = nullptr;
        m_transformRotationToProperty.remove(property);
    }
    else if (m_transformScaleToProperty.contains(property))
    {
        auto tProperty = m_transformScaleToProperty.value(property);
        m_propertyTotransformData[tProperty].scale = nullptr;
        m_transformScaleToProperty.remove(property);
    }
    else if (m_transformXOffsetToProperty.contains(property))
    {
        auto tProperty = m_transformXOffsetToProperty.value(property);
        m_propertyTotransformData[tProperty].x = nullptr;
        m_transformXOffsetToProperty.remove(property);
    }
    else if (m_transformYOffsetToProperty.contains(property))
    {
        auto tProperty = m_transformYOffsetToProperty.value(property);
        m_propertyTotransformData[tProperty].y = nullptr;
        m_transformYOffsetToProperty.remove(property);
    }
}

QString PropertyManager::valueText(const QtProperty *property) const
{
    if (m_propertyTotransformData.contains(property))
    {
        QVariant variant = m_propertyTotransformData[property].value;
        QTransform transform = variant.value<QTransform>();
        QLineF xVector = transform.map(QLineF(0, 0, 1, 0));
        QLineF yVector = transform.map(QLineF(0, 0, 0, 1));
        bool mirrored = !qFuzzyCompare(360.0 - xVector.angleTo(yVector), 90.0);
        qreal rotation = std::fmod(360.0 - xVector.angle(), 360.0);
        qreal scale = xVector.length();
        if (mirrored)
            rotation = std::fmod(180.0 - rotation + 360.0, 360.0);
        //const TransformData &data = m_propertyTotransformData[property];
        return QString("(%1, %2, %3, %4, %5)")
                .arg(transform.dx())
                .arg(transform.dy())
                .arg(rotation)
                .arg(mirrored ? "true" : "false")
                .arg(scale);
    }
    return QtVariantPropertyManager::valueText(property);
}

void PropertyManager::initializeProperty(QtProperty *property)
{
    if (propertyType(property) == transformTypeId())
    {
        TransformData data;
        data.value = QTransform();

        data.x = addProperty(QVariant::Double);
        data.x->setPropertyName("ΔX");
        property->addSubProperty(data.x);
        m_transformXOffsetToProperty.insert(data.x, property);

        data.y = addProperty(QVariant::Double);
        data.y->setPropertyName("ΔY");
        property->addSubProperty(data.y);
        m_transformYOffsetToProperty.insert(data.y, property);

        data.rotation = addProperty(QVariant::Double);
        data.rotation->setPropertyName("Rotation");
        property->addSubProperty(data.rotation);
        m_transformRotationToProperty.insert(data.rotation, property);

        data.mirror = addProperty(QVariant::Bool);
        data.mirror->setPropertyName("Mirror");
        property->addSubProperty(data.mirror);
        m_transformMirrorToProperty.insert(data.mirror, property);

        data.scale = addProperty(QVariant::Double);
        data.scale->setPropertyName("Scale");
        property->addSubProperty(data.scale);
        m_transformScaleToProperty.insert(data.scale, property);

        m_propertyTotransformData.insert(property, data);
    }
    QtVariantPropertyManager::initializeProperty(property);
}

void PropertyManager::uninitializeProperty(QtProperty *property)
{
    if (propertyType(property) == transformTypeId())
    {
        TransformData data = m_propertyTotransformData.value(property);
        m_transformXOffsetToProperty.remove(data.x);
        m_transformYOffsetToProperty.remove(data.y);
        m_transformMirrorToProperty.remove(data.mirror);
        m_transformRotationToProperty.remove(data.rotation);
        m_transformScaleToProperty.remove(data.scale);
        m_propertyTotransformData.remove(property);
    }
    QtVariantPropertyManager::uninitializeProperty(property);
}

bool PropertyManager::isPropertyTypeSupported(int propertyType) const
{
    if (propertyType == transformTypeId())
        return true;
    return QtVariantPropertyManager::isPropertyTypeSupported(propertyType);
}

int PropertyManager::valueType(int propertyType) const
{
    if (propertyType == transformTypeId())
        return QVariant::Transform;
    return QtVariantPropertyManager::valueType(propertyType);
}

QVariant PropertyManager::value(const QtProperty *property) const
{
    if (m_propertyTotransformData.contains(property))
        return m_propertyTotransformData[property].value;
    return QtVariantPropertyManager::value(property);
}

void PropertyManager::setValue(QtProperty *property, const QVariant &value)
{
    if (m_propertyTotransformData.contains(property))
    {
        if (value.type() != transformTypeId() && !value.canConvert(transformTypeId()))
            return;
        QTransform transform = value.value<QTransform>();
        QLineF xVector = transform.map(QLineF(0, 0, 1, 0));
        QLineF yVector = transform.map(QLineF(0, 0, 0, 1));
        bool mirrored = !qFuzzyCompare(360.0 - xVector.angleTo(yVector), 90.0);
        qreal rotation = std::fmod(360.0 - xVector.angle(), 360.0);
        qreal scale = xVector.length();
        if (mirrored)
            rotation = std::fmod(180.0 - rotation + 360.0, 360.0);
        TransformData &data = m_propertyTotransformData[property];
        data.value = transform;
        data.x->setValue(transform.dx());
        data.y->setValue(transform.dy());
        data.mirror->setValue(mirrored);
        data.rotation->setValue(rotation);
        data.scale->setValue(scale);

        emit propertyChanged(property);
        emit valueChanged(property, transform);
        return;
    }
    QtVariantPropertyManager::setValue(property, value);
}
